/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhike
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.fbank.marshall;

import com.acooly.core.utils.Reflections;
import com.acooly.core.utils.Strings;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.provider.fbank.FbankConstants;
import com.acooly.module.openapi.client.provider.fbank.OpenAPIClientFbankProperties;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankApiMessage;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankRequest;
import com.acooly.module.openapi.client.provider.fbank.support.FbankAlias;
import com.acooly.module.openapi.client.provider.fbank.utils.JsonMarshallor;
import com.acooly.module.safety.Safes;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignerFactory;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Field;
import java.util.Set;
import java.util.SortedMap;

/**
 * @author zhike
 */
@Slf4j
public class FbankMarshallSupport {

    @Autowired
    protected OpenAPIClientFbankProperties openAPIClientFbankProperties;

    @Autowired
    protected SignerFactory signerFactory;

    @Autowired
    private KeyLoadManager keyStoreLoadManager;

    public static JsonMarshallor jsonMarshallor = JsonMarshallor.INSTANCE;

    protected OpenAPIClientFbankProperties getProperties() {
        return openAPIClientFbankProperties;
    }

    public SignerFactory getSignerFactory() {
        return signerFactory;
    }

    protected String doMarshall(FbankRequest source) {
        doVerifyParam(source);
        SortedMap<String, String> requestDataMap = getRequestDataMap(source);
        log.debug("待签字符串:{}", requestDataMap);
        source.setSignature(doSign(requestDataMap, source));
        requestDataMap.put(FbankConstants.SIGN_KEY, source.getSignature());
        return jsonMarshallor.marshall(requestDataMap);
    }

    /**
     * 获取代签map
     *
     * @param source
     * @return
     */
    protected SortedMap<String, String> getRequestDataMap(FbankRequest source) {
        SortedMap<String, String> requestData = Maps.newTreeMap();
        Set<Field> fields = Reflections.getFields(source.getClass());
        String key = null;
        Object value = null;
        for (Field field : fields) {
            value = Reflections.getFieldValue(source, field.getName());
            if (value == null) {
                continue;
            }
            FbankAlias fbankAlias = field.getAnnotation(FbankAlias.class);
            if (fbankAlias == null) {
                key = field.getName();
            } else {
                if (!fbankAlias.sign()) {
                    continue;
                }
                key = fbankAlias.value();
            }
            if (Strings.isNotBlank((String) value)) {
                requestData.put(key, (String) value);
            }
        }
        return requestData;
    }

    /**
     * 将对象转化为String
     *
     * @param object
     * @return
     */
    private String convertString(Object object) {
        if (object == null) {
            return null;
        }
        return object.toString();
    }

    /**
     * 校验参数
     *
     * @param source
     */
    protected void doVerifyParam(FbankApiMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }

    /**
     * 签名 优先获取传进来的，如果传进来没有则用配置文件中的
     *
     * @param waitForSign
     * @param source
     * @return
     */
    protected String doSign(SortedMap<String, String> waitForSign, FbankRequest source) {
        String key = getKeyStore(source.getMchntId());
        String signStr = Safes.getSigner(FbankConstants.SIGNER_TYPE).sign(waitForSign, key);
        return signStr;
    }

    protected String doSign(SortedMap<String, String> waitForSign, String key) {
        String signStr = Safes.getSigner(FbankConstants.SIGNER_TYPE).sign(waitForSign, key);
        return signStr;
    }

    /**
     * 验签
     *
     * @param signature
     * @param plain
     * @return
     */
    protected void doVerifySign(SortedMap<String, String> plain, String signature, String partnerId) {
        Safes.getSigner(FbankConstants.SIGNER_TYPE).verify(plain, getKeyStore(partnerId), signature);
    }


    /**
     * 获取keyStoreInfo
     *
     * @return
     */
    protected String getKeyStore(String partnerId) {
        try {
            return keyStoreLoadManager.load(partnerId, FbankConstants.PROVIDER_NAME);
        } catch (Exception e) {
            log.info("商户partnerId={}密钥加载失败,启用配置文件密钥", partnerId);
            return openAPIClientFbankProperties.getMd5Key();
        }
    }

    protected void beforeMarshall(FbankApiMessage message) {
        if (Strings.isBlank(message.getMchntId())) {
            message.setMchntId(getProperties().getPartnerId());
        }
    }
}
